var Skink; if (!Skink) Skink = {};

/*************
Skink.Data
*************/
if (!Skink.Data) Skink.Data = {};

$.ajaxSetup({
    timeout: DEFAULT_TIMEOUT,
    async: true,
    // type:'POST'
    // cache:false
});

Skink.Data.getAPIURL = function(key) {
    var base_url = Skink.Prefs.get('twitter-api-base-url');

    if (!base_url) {
        base_url = 'https://twitter.com/';
    }

    var urls = {}

    // Timeline URLs
    urls.public_timeline = "statuses/public_timeline.json";
    urls.friends_timeline = "statuses/friends_timeline.json";
    urls.user_timeline = "statuses/user_timeline.json";
    urls.replies_timeline = "statuses/replies.json";
    urls.show = "statuses/show.json";
    urls.favorites = "favorites.json";
    urls.dm_timeline = "direct_messages.json";
    urls.dm_sent = "direct_messages/sent.json";
    urls.friendslist = "statuses/friends.json";
    urls.followerslist = "statuses/followers.json";
    urls.featuredlist = "statuses/featured.json";

    // Action URLs
    urls.update = "statuses/update.json";
    urls.destroy_status = "statuses/destroy/{{ID}}.json";
    urls.follow = "friendships/create/{{ID}}.json";
    urls.stop_follow = "friendships/destroy/{{ID}}.json";
    urls.start_notifications = "notifications/follow/{{ID}}.json";
    urls.stop_notifications = "notifications/leave/{{ID}}.json";
    urls.favorites_create = "favourings/create/{{ID}}.json";
    urls.favorites_destroy = "favourings/destroy/{{ID}}.json";
    urls.verify_password = "account/verify_credentials.json";
    urls.ratelimit_status = "account/rate_limit_status.json";

    // misc
    urls.test = "help/test.json";
    urls.downtime_schedule = "help/downtime_schedule.json";

    if (urls[key]) {
        // air.trace("URL:"+base_url + urls[key]);
        return base_url + urls[key];
    } else {
        return false
    }

};

/**
temp storage for a section's ajax queries
 */
Skink.Data.$ajaxQueueStorage = [];

/**
Errors recorded during ajax queries
 */
Skink.Data.$ajaxQueueErrors = [];

/**
counter for # of finished ajax queries in a section
 */
Skink.Data.$ajaxQueueFinished = 0;

/**
 * Verifies the username and password in the prefs fields against the Twitter API
 * @returns void
 */
Skink.Data.verifyPassword = function() {
    var user = $('#username').val();
    var pass = $('#password').val();

    Skink.dump('user:' + user + ' pass:********');

    Skink.UI.statusBar("Verifying username and password");
    Skink.UI.showLoading();

    var xhr = $.ajax({
        complete: Skink.Data.onAjaxComplete,
        error: function() {
            // Skink.verified = false;
            Skink.dump('verification failed');
            Skink.UI.statusBar("Verification failed");
            Skink.UI.flashStatusBar();
            Skink.Data.onAjaxError();
        },
        success: function(data) {
            air.trace('verified; setting current user');
            Skink.Prefs.setCurrentUser();
            Skink.UI.statusBar("Verification succeeded");
            Skink.UI.flashStatusBar();

            if (Skink.Prefs.get('network-autoadjustrefreshinterval')) {
                Skink.Data.getRateLimitInfo(Skink.Prefs.setRateLimit);
            }
        },
        beforeSend: function(xhr) {
            xhr.setRequestHeader("Authorization", "Basic " + Base64.encode(user + ":" + pass));
            // cookies just get in the way.	 eliminate them.
            xhr.setRequestHeader("Cookie", "");
        },
        processData: false,
        type: "POST",
        url: Skink.Data.getAPIURL('verify_password'),
    })
}

/**
 * send a status update to Twitter
 * @param {String} msg the message to be posted
 * @param {String} username the username
 * @param {String} password the password
 * @returns void
 */
Skink.Data.update = function(msg, username, password, irt_id) {
    var user = username;
    var pass = password;

    Skink.dump('user:' + user + ' pass:********');

    Skink.UI.statusBar("Sending update");
    Skink.UI.showLoading();

    $('#entrybox').attr('disabled', true);
    $('#updateButton').attr('disabled', true);
    var oldButtonLabel = $('#updateButton').val();
    $('#updateButton').val('Sending...');

    var update_data = "&source=" + Skink.Prefs.get('twitter-source') + "&status=" + encodeURIComponent(msg);
    if (irt_id) {
        update_data = update_data + "&in_reply_to_status_id=" + irt_id;
    }

    var xhr = $.ajax({
        timeout: UPDATE_TIMEOUT,
        complete: Skink.Data.onAjaxComplete,
        error: function(xhr, rstr) {
            Skink.dump("ERROR");
            $('#entrybox').attr('disabled', false);
            $('#updateButton').attr('disabled', false);
            $('#updateButton').val(oldButtonLabel);

            if (xhr.readyState < 3) {
                Skink.dump("Update ERROR: Server did not confirm update");
                Skink.UI.statusBar("ERROR: Server did not confirm update")
                return;
            }

            if (xhr.status != 200) { // sanity check
                Skink.dump("ERROR: " + rstr);
                Skink.UI.statusBar("ERROR: Server could not post update");
                Skink.UI.flashStatusBar();
            } else {

}
        },
        success: function(data) {
            Skink.dump('SUCCESS:' + data);
            $('#entrybox').attr('disabled', false);
            $('#updateButton').attr('disabled', false);
            $('#entrybox').val('');
            Skink.dump('Emptied #entrybox');
            $('#updateButton').val(oldButtonLabel);
            Skink.dump('reset #updateButton label');
            Skink.UI.playSoundUpdate();
            Skink.UI.statusBar("Update succeeded");
            var entry = JSON.parse(data);

            // We mark it as read in the db
            Skink.DB.markEntryAsRead(entry.id);

            Skink.UI.entryBox.reset();
            Skink.UI.clearPostIRT();
            Skink.dump('reset entryBox (Spry)');
            $('#entrybox')[0].blur();
            Skink.dump('Blurred entryBox (DOM)');
        },
        beforeSend: function(xhr) {
            xhr.setRequestHeader("Authorization", "Basic " + Base64.encode(user + ":" + pass));
            // cookies just get in the way.	 eliminate them
            xhr.setRequestHeader("Cookie", '');
            // have to kill referer header to post
        },
        processData: false,
        type: "POST",
        url: Skink.Data.getAPIURL('update'),
        data: update_data,
        //		data:"&status="+encodeURIComponent(msg),
    });
}

/**
 * Deletes the given status
 * @param {Number} postid the id of the post to delete
 * @returns void
 */
Skink.Data.destroyStatus = function(postid) {
    var user = Skink.Prefs.getUser();
    var pass = Skink.Prefs.getPass();

    Skink.UI.showLoading();

    var xhr = $.ajax({
        complete: Skink.Data.onAjaxComplete,
        error: Skink.Data.onAjaxError,
        success: function(data) {
            Skink.dump(data);
            Skink.UI.statusBar("Status deleted");
            //Skink.Data.loadUserTimelineData('tab-user');
        },
        beforeSend: function(xhr) {
            xhr.setRequestHeader("Authorization", "Basic " + Base64.encode(user + ":" + pass));
            // cookies just get in the way.	 eliminate them
            xhr.setRequestHeader("Cookie", "");
            xhr.setRequestHeader("If-Modified-Since", 'Sun, 1 Jan 2007 18:54:41 GMT');
        },
        processData: false,
        type: "POST",
        data: '&id=' + postid,
        url: Skink.Data.getAPIURL('destroy_status').replace(/{{ID}}/, postid),
    });
}

/**
 * Marks the given post as a "favorite" of the current user
 * @param {Number} postid the id of the post to favorite
 * @returns void
 */
Skink.Data.makeFavorite = function(postid) {
    var user = Skink.Prefs.getUser();
    var pass = Skink.Prefs.getPass();

    Skink.UI.statusBar('Adding fav: ' + postid);
    Skink.UI.showLoading();

    var xhr = $.ajax({
        complete: Skink.Data.onAjaxComplete,
        error: Skink.Data.onAjaxError,
        success: function(data) {
            var faved_element;
            Skink.dump(data);
            Skink.UI.statusBar('Added fav: ' + postid);
            if (faved_element == Skink.UI.getElementFromStatusId(postid)) {
                $(faved_element).addClass('favorited');
            }
            //Skink.Data.loadUserTimelineData('tab-user');
        },
        beforeSend: function(xhr) {
            xhr.setRequestHeader("Authorization", "Basic " + Base64.encode(user + ":" + pass));
            // cookies just get in the way.	 eliminate them
            xhr.setRequestHeader("Cookie", "");
            xhr.setRequestHeader("If-Modified-Since", 'Sun, 1 Jan 2007 18:54:41 GMT');
        },
        processData: false,
        type: "POST",
        data: '&id=' + postid,
        url: Skink.Data.getAPIURL('favorites_create').replace(/{{ID}}/, postid)
    });
};

/**
 * Un-marks the given post as a "favorite" of the current user
 * @param {Number} postid the id of the post to un-favorite
 * @returns void
 */
Skink.Data.makeNotFavorite = function(postid) {
    var user = Skink.Prefs.getUser();
    var pass = Skink.Prefs.getPass();

    Skink.UI.statusBar('Removing fav: ' + postid);
    Skink.UI.showLoading();

    var xhr = $.ajax({
        complete: Skink.Data.onAjaxComplete,
        error: Skink.Data.onAjaxError,
        success: function(data) {
            var faved_element;
            Skink.dump(data);
            Skink.UI.statusBar('Removed fav: ' + postid);
            if (faved_element = Skink.UI.getElementFromStatusId(postid)) {
                $(faved_element).removeClass('favorited');
            }
            //Skink.Data.loadUserTimelineData('tab-user');
        },
        beforeSend: function(xhr) {
            xhr.setRequestHeader("Authorization", "Basic " + Base64.encode(user + ":" + pass));
            // cookies just get in the way.	 eliminate them
            xhr.setRequestHeader("Cookie", "");
            xhr.setRequestHeader("If-Modified-Since", 'Sun, 1 Jan 2007 18:54:41 GMT');
        },
        processData: false,
        type: "POST",
        data: '&id=' + postid,
        url: Skink.Data.getAPIURL('favorites_destroy').replace(/{{ID}}/, postid),
    });
};

/**
 * Follows the passed userid
 * @param {String} userid the userid to follow
 * @returns void
 */
Skink.Data.followUser = function(userid) {
    var user = Skink.Prefs.getUser();
    var pass = Skink.Prefs.getPass();

    Skink.dump('user:' + user + ' pass:********');

    Skink.UI.statusBar('Start following: ' + userid)
    Skink.UI.showLoading();

    var xhr = $.ajax({
        complete: Skink.Data.onAjaxComplete,
        error: Skink.Data.onAjaxError,
        success: function(data) {
            Skink.dump(data);
            Skink.UI.setSelectedTab(document.getElementById(Skink.Section.friends.tab));
            Skink.UI.reloadCurrentTab();
            Skink.UI.statusBar("Now following " + userid);
        },
        beforeSend: function(xhr) {
            xhr.setRequestHeader("Authorization", "Basic " + Base64.encode(user + ":" + pass));
            // cookies just get in the way.	 eliminate them
            xhr.setRequestHeader("Cookie", "");
            xhr.setRequestHeader("If-Modified-Since", 'Sun, 1 Jan 2007 18:54:41 GMT');
        },
        processData: false,
        type: "POST",
        data: '&id=' + userid,
        url: Skink.Data.getAPIURL('follow').replace(/{{ID}}/, userid),
    });
};

/**
 * Stop following the passed userid
 * @param {String} userid the userid to stop following
 * @returns void
 */
Skink.Data.stopFollowingUser = function(userid) {
    var user = Skink.Prefs.getUser();
    var pass = Skink.Prefs.getPass();

    Skink.dump('user:' + user + ' pass:********');

    Skink.UI.statusBar('Stop following: ' + userid)
    Skink.UI.showLoading();

    var xhr = $.ajax({
        complete: Skink.Data.onAjaxComplete,
        error: Skink.Data.onAjaxError,
        success: function(data) {
            Skink.dump(data);
            Skink.UI.setSelectedTab(document.getElementById(Skink.Section.friends.tab));
            Skink.UI.reloadCurrentTab();
            Skink.UI.statusBar("No longer following " + userid);
        },
        beforeSend: function(xhr) {
            xhr.setRequestHeader("Authorization", "Basic " + Base64.encode(user + ":" + pass));
            // cookies just get in the way.	 eliminate them
            xhr.setRequestHeader("Cookie", "");
            xhr.setRequestHeader("If-Modified-Since", 'Sun, 1 Jan 2007 18:54:41 GMT');
        },
        processData: false,
        type: "POST",
        data: '&id=' + userid,
        url: Skink.Data.getAPIURL('stop_follow').replace(/{{ID}}/, userid),
    });
};

/**
 * Called by most of the Twitter ajax methods when complete
 * @param {Object} xhr the xhr object
 * @param {rstr} xhr a "response" string that indicates if the request was successful or not
 * @returns void
 */
Skink.Data.onAjaxComplete = function(xhr, rstr) {
    Skink.UI.hideLoading();
    if (xhr.readyState < 3) {
        Skink.dump("ERROR: Server did not respond");
        Skink.UI.statusBar("ERROR: Server did not respond")
        return;
    }
    Skink.dump("HEADERS:\n" + xhr.getAllResponseHeaders());
    Skink.dump("DATA:\n" + xhr.responseText);
    Skink.dump("COMPLETE: " + rstr);
};

/**
 * Called by most of the Twitter ajax methods on error
 * @param {Object} xhr the xhr object
 * @param {rstr} xhr a "response" string that indicates if the request was successful or not
 * @returns void
 */
Skink.Data.onAjaxError = function(xhr, rstr) {
    Skink.dump("ERROR: " + rstr);
    if (xhr.readyState < 3) {
        Skink.dump("ERROR: Server did not respond.");
    }
    if (xhr.responseText) {
        try {
            var errorInfo = JSON.parse(xhr.responseText)
            if (errorInfo.error) {
                Skink.UI.statusBar('ERROR: "' + errorInfo.error + '"');
            } else {
                Skink.UI.statusBar('ERROR: Server returned invalid data');
            }
        } catch(e) {
            Skink.dump('Error parsing for JSON in error response');
            Skink.UI.statusBar('ERROR: Server returned invalid data');
        }
    }
    Skink.UI.flashStatusBar();
};

/**
 * Starts the process of data retrieval for a section
 * @param {object} section the Skink.Section object
 * @param {boolean} force if true, forces a reload of the section even if mincachetime has not passed
 * @returns void
 */
Skink.Data.getDataForTimeline = function(section, force) {
    var username = Skink.Prefs.getUser();
    if (!username || username == 'null' || username == 'undefined' || username == 'false') {

        $('#timeline-friends').html("<div id='not-logged-in'><div>Username and password not set.</div><input type='button' id='open-login-panel' value='Enter user/pass' /> </div>");

        $('#open-login-panel').one('click',
        function() {
            $('#not-logged-in').remove();
            Skink.UI.showPrefs();
            setTimeout(Skink.UI.openLoginPanel, 500);
        });

        Skink.dump('hiding loading');
        Skink.UI.hideLoading();
        return;
    } else {
        $('#not-logged-in').remove();
    }

    Skink.dump('now:' + getTimeAsInt());
    Skink.dump('then:' + section.lastcheck);
    Skink.dump('difference:' + (getTimeAsInt() - section.lastcheck));
    Skink.dump('section.mincachetime:' + section.mincachetime);

    if (force || (getTimeAsInt() - section.lastcheck) > section.mincachetime) {
        section.lastcheck = getTimeAsInt();

        for (var i = 0; i < section.urls.length; i++) {
            // alert('section.urls['+i+']: '+ section.urls[i])
            Skink.Data.getDataForUrl(section.urls[i], section);
            // data = data.concat(thisdata);
        }
    } else {
        Skink.dump('Not loading data - section.mincachetime has not expired');
    }
}

Skink.Data.onSectionAjaxComplete = function(section, thisurl, xhr, msg) {
    Skink.Data.$ajaxQueueFinished++;

    if (xhr.readyState < 3) { // XHR is not yet ready. don't try to access response headers
        Skink.dump("Error:Timeout on " + thisurl);
    } else {
        Skink.dump("HEADERS:\n" + xhr.getAllResponseHeaders());
        Skink.dump("STATUS:\n" + xhr.status);
        Skink.dump("DATA:\n" + xhr.responseText);
        Skink.dump("COMPLETE: " + msg);

        if (xhr.status == 400) {
            Skink.dump("ERROR: 400 error - Exceeded request limit. Response from Twitter:\n" + xhr.responseText);
        }

        else if (xhr.status == 401) {
            Skink.dump("ERROR: 401 error - Not Authenticated. Check your username and password.	Response from Twitter:\n" + xhr.responseText);
            Skink.Data.$ajaxQueueErrors.push("Not Authenticated. Check your username and password.");
        }

        else if (xhr.responseText.length < 0) {
            Skink.dump("Error:response empty from " + thisurl);
            Skink.Data.$ajaxQueueErrors.push("Empty response from server for " + thisurl)
        }

        try {
            var data = parseJSON(xhr.responseText);
            Skink.dump(typeof(data))

            if (!data) {
                Skink.dump("Error: no data returned from " + thisurl);
            } else {
                /* This is a little hack for summize data */
                if (data.results) {
                    data = data.results;
                }
                if (data.error) {
                    Skink.dump("ERROR: " + data.error + " [" + data.request + "]");
                    Skink.Data.$ajaxQueueErrors.push("Twitter says: \"" + data.error + "\"");
                } else {
                    Skink.Data.$ajaxQueueStorage = Skink.Data.$ajaxQueueStorage.concat(data);
                }
            }
        } catch(e) {
            Skink.dump("An exception occurred when eval'ing the returned data. Error name: " + e.name + ". Error message: " + e.message);
        }

        Skink.dump('Skink.Data.$ajaxQueueFinished:' + Skink.Data.$ajaxQueueFinished);
        Skink.dump('section.urls.length:' + section.urls.length);
        Skink.dump('Skink.Data.$ajaxQueueStorage.length:' + Skink.Data.$ajaxQueueStorage.length);
        Skink.dump('Skink.Data.$ajaxQueueErrors.length:' + Skink.Data.$ajaxQueueErrors.length);

    }

    if (Skink.Data.$ajaxQueueFinished >= section.urls.length) {
        Skink.Data.$ajaxQueueFinished = 0;
        Skink.UI.statusBar('Adding ' + Skink.Data.$ajaxQueueStorage.length + ' entries');

        if (Skink.Data.$ajaxQueueStorage.length > 0) {
            time.start('addingItems');
            for (var i in Skink.Data.$ajaxQueueStorage) {
                // air.trace('URL:'+thisurl);
                // air.trace('section URLs:'+section.urls);
                /*
					Check the origin URL to see if this is a follower or someone the
					user is following
				*/
                if (thisurl == Skink.Data.getAPIURL('friendslist')) {
                    Skink.Data.$ajaxQueueStorage[i].is_following = true;
                } else if (thisurl == Skink.Data.getAPIURL('followerslist')) {
                    Skink.Data.$ajaxQueueStorage[i].is_follower = true;
                }

                section.addItem(Skink.Data.$ajaxQueueStorage[i]);
            }
            time.stop('addingItems');
        }

        Skink.dump('onSectionAjaxComplete cleaning up timeline');
        section.cleanup();

        Skink.dump('hiding loading');
        Skink.UI.hideLoading();

        if (Skink.Data.$ajaxQueueErrors.length > 0) {
            var errors = Skink.Data.$ajaxQueueErrors.join("\n");
            Skink.UI.alert(errors, "Error");
            Skink.dump(errors);
            Skink.Data.$ajaxQueueErrors = [];
        }

        Skink.dump('emptying storage');
        Skink.Data.$ajaxQueueStorage = [];
    }
}

/**
 * Retrieves data for the given URL, as part of a queue. Used by Skink.Data.getDataForTimeline
 * @param {String} url The URL to request data from
 * @param {Object} section The Skink.Section that is this request is getting data for
 * @returns void
 */
Skink.Data.getDataForUrl = function(url, section) {
    Skink.dump('getting:' + url);

    Skink.dump('section.timeline:' + section.timeline);

    Skink.UI.statusBar("Checking for new data…");
    Skink.UI.showLoading();

    var xhr = $.ajax({
        // mode:'queue',
        complete: function(xhr, msg) {
            air.trace('Getting data for url:' + url);
            section.onAjaxComplete(url, xhr, msg);
        },
        error: function(xhr, msg, exc) {
            if (xhr && xhr.responseText) {
                Skink.dump("Error:" + xhr.responseText + " from " + url);
            } else {
                Skink.dump("Error:Unknown from " + url);
            }
        },
        beforeSend: function(xhr) {
            var user = Skink.Prefs.getUser();
            var pass = Skink.Prefs.getPass();
            xhr.setRequestHeader("Authorization", "Basic " + Base64.encode(user + ":" + pass));
            xhr.setRequestHeader("Cookie", '');
        },
        processData: false,
        type: "GET",
        url: url,
        data: null
    });
}

Skink.Data.getRateLimitInfo = function(callback, cbdata) {
    var user = Skink.Prefs.getUser();
    var pass = Skink.Prefs.getPass();

    if (!user || !pass) {
        air.trace('Dropping out of getRateLimitInfo because user or pass is not set');
        return false;
    }

    Skink.UI.showLoading();
    Skink.UI.statusBar('Asking Twitter for rate limit info…');

    var xhr = $.ajax({
        complete: Skink.Data.onAjaxComplete,
        error: Skink.Data.onAjaxError,
        success: function(data) {
            air.trace(data);

            json = JSON.parse(data);

            if (callback) {
                callback(json, cbdata);
            }
        },
        beforeSend: function(xhr) {
            xhr.setRequestHeader("Authorization", "Basic " + Base64.encode(user + ":" + pass));
            xhr.setRequestHeader("Cookie", "");
            xhr.setRequestHeader("If-Modified-Since", 'Sun, 1 Jan 2007 18:54:41 GMT');
        },
        processData: false,
        type: "GET",
        url: Skink.Data.getAPIURL('ratelimit_status'),
    });
}

/**
 * loads data for a particular tab (tabs are usually connected to a single Skink.Section)
 * @param {Object} tab the DOM Element of the tab
 * @param {Boolean} force if true, force a reload even if mincachetime of this tab's section has not expired
 * @param {Boolean} reset resets all lastid/mincachetime data on this section
 * @returns false
 * @type Boolean
 * @see Skink.Section.getSectionFromTab
 */

Skink.Data.loadDataForTab = function(tab, force, reset) {
    if (!force) {
        force = false;
    }

    if (!reset) {
        reset = false;
    }

    Skink.dump('Loading data for tab:' + tab.id);
    var section = Skink.Section.getSectionFromTab(tab)
    Skink.dump('SECTION:' + section);
    Skink.dump('load data for tab ' + tab.id);
    switch (tab.id) {
    case 'tab-prefs':
        break;
    default:
        section.build(force, reset);
        break;
    }
    return false
};